#pragma once
#include <iostream>
#include <string>
#include <filesystem> //c++17的文件操作
#include <fstream>
#include <ctime>
#include <unistd.h>
#include <memory>
#include <sstream>
#include "mutex.hpp"
using namespace std;
// 枚举出来错误的类型

enum class LogLevel
{
    DEBUG,
    INFO,
    WARNING,
    ERRPR,
    FATAL
};

string Level2String(LogLevel level)
{
    switch (level)
    {
    case LogLevel::DEBUG:
        return "DEBBUG";
    case LogLevel::INFO:
        return "INFO";
    case LogLevel::WARNING:
        return "WARNING";
    case LogLevel::ERRPR:
        return "ERRPR";
    case LogLevel::FATAL:
        return "FATAl";
    default:
        return "UNKONW";
    }
}
std::string GetCurrentTime()
{
    // 1. 获取时间戳
    time_t currtime = time(nullptr);

    // 2. 如何把时间戳转换成为20XX-08-04 12:27:03
    struct tm currtm; // 返回一个结构体
    localtime_r(&currtime, &currtm);

    // 3. 转换成为字符串 -- dubug?
    char timebuffer[64];
    snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d",
             currtm.tm_year + 1900, // 加1900 是因为默认从1900开始为1
             currtm.tm_mon + 1,
             currtm.tm_mday,
             currtm.tm_hour,
             currtm.tm_min,
             currtm.tm_sec); // 格式化打印

    return timebuffer;
}
class LogStrategy
{
    // C++11可以让你更好的控制要使⽤的默认函数。假设你要使⽤某个默认的函数，但是因为⼀些原因
    //  这个函数没有默认⽣成。⽐如：我们提供了拷⻉构造，就不会⽣成移动构造了，那么我们可以使⽤
    //  default关键字显⽰指定移动构造⽣成。
public:
    virtual ~LogStrategy() = default; // 这里的含义是 生成默认的析构函数 因为派生类析构的时候会调用基类的那部分
    // 析构函数析构基类的那部分不然会报错
    virtual void SyncLog(const std::string &logmessage) = 0;
    // 后面的=0表示这是一个纯虚函数 包含至少一个纯虚函数的叫抽象类 抽象类不可以实例化 同时派生类必须实现这个虚函数 不让也会变成抽象类
};
// 显示器的刷新策略
class ConsoleLogStrategy : public LogStrategy
{
public:
    void SyncLog(const std::string &logmessage) override
    {
        {
            lockgurd lockguard(&_lock);
            std::cout << logmessage << std::endl;
        }
    }
    ~ConsoleLogStrategy()
    {
    }

private:
    Mutex _lock;
};
// 以后改文件只要在这里修改就可以了
const string logdefaultdir = "log";                // 文件目录
const static std::string logfilename = "test.log"; // 文件目录

// 文件刷新
class FileLogStrategy : public LogStrategy
{
public:
    FileLogStrategy(const string &dir = logdefaultdir, const string &filename = logfilename)
        : _dir_path_name(dir), _filenmae(filename)
    {
        // 这段代码不是很理解，等复习完c++抛异常之后再来看
        if (filesystem::exists(_dir_path_name))
        {
            return;
        }
        try
        {
            std::filesystem::create_directories(_dir_path_name);
        }
        catch (const std::filesystem::filesystem_error &e)
        {
            std::cerr << e.what() << '\n';
        }
    }
    void SyncLog(const std::string &logmessage) override // override 检测虚函数是否需要重写
    {
        {
            lockgurd lockguard(&_lock);
            // 构建出文件目录
            string target = _dir_path_name;
            target += "/";
            target += _filenmae;
            // c_str() 返回的是一个 以 \0 结尾的 C 字符串，可直接传递给需要 const char* 的 C 函数（如 printf、strlen）。
            ofstream out(target.c_str(), ios::app); // append 相当于以文件追加的方式打开
            if (!out.is_open())
            {
                return;
            }
            out << logmessage << "\n";
            out.close();
        }
    }

private:
    Mutex _lock;
    string _dir_path_name;
    string _filenmae;
};

// 2.构建完整的日志
class Logger
{
public:
    Logger()
    {
    }
    void EnableConsoleLogStrategy()
    {
        // 使用屏幕的刷新侧路 然后用基类的指针指向派生类
        // 关于继承的指针问题。
        // public继承的派生类可以赋值给基类的指针或者引用
        _strategy = std::make_unique<ConsoleLogStrategy>();
    }
    void EnableFileLogStrategy()
    {
        _strategy = std::make_unique<FileLogStrategy>();
    }
    // 形成一条完整的日志
    class Logmessage
    {
    public:
        Logmessage(LogLevel level, string &filename, int line, Logger &logger)
            : _curr_time(GetCurrentTime()),
              _level(level),
              _pid(getpid()),
              _line(line),
              _logger(logger)
        {
            stringstream ss;
            ss << "[" << _curr_time << "] "
               << "[" << Level2String(_level) << "] "
               << "[" << _pid << "] "
               << "[" << _filename << "] "
               << "[" << _line << "]"
               << " - ";
            _loginfo = ss.str();
        }
        template <typename T>
        Logmessage &operator<<(const T &info)
        {
            stringstream ss;
            ss << info;
            _loginfo += ss;
            return *this
        }
        ~Logmessage()
        {
            if (_logger._strategy)
            {
                _logger._strategy->SyncLog(_loginfo);
            }
        }

    private:
        string _curr_time; // 日志时间
        LogLevel _level;   // 日志等级
        pid_t _pid;
        string _filename;
        int _line; // 哪一行出现问题

        std::string _loginfo; // 一条合并完成的，完整的日志信息
        Logger &_logger;      // 提供刷新策略的具体做法
    };
    Logmessage operator()(LogLevel level, string filename, int line)
    {
        return Logmessage(level, filename, line, *this);
    }
    ~Logger()
    {
        
    }

private:
    // 智能指针不太理解 复习完再补充
    unique_ptr<LogStrategy> _strategy;
};
Logger logger;
#define LOG(level) logger(level, __FILE__, __LINE__)
#define EnableConsoleLogStrategy() logger.EnableConsoleLogStrategy()
#define EnableFileLogStrategy() logger.EnableFileLogStrategy()

// __FILE__（当前文件名）
// 预定义宏，由编译器自动替换为当前源文件的完整路径或文件名（字符串）。

//  __LINE__（当前行号）
// 预定义宏，由编译器自动替换为宏调用处的行号（整数）。